﻿using Jt808DataParser.Model;
using Jt808DataParser.Utils;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Jt808DataParser
{
    public class DataParser
    {
        //Last Unprocessed Bytes Remaining
        private static byte[] endbytes = null;

        /// <summary>
        /// Main method(This method is called directly in the form of hex string after receiving the device message)
        /// Parse hex raw data
        /// </summary>
        /// <param name="strData"></param>
        /// <returns></returns>
        public static string receiveData(string strData)
        {
            strData = strData.Replace("\n", "").Replace("\r\n", "").Trim();
            byte[] bytes = CommonUtil.HexStringToBytes(strData.Replace(" ", ""));
            if (bytes == null || bytes.Length == 0) {
                return null;
            }
            return receiveData(bytes);
        }

        /// <summary>
        /// Main method(This method is called directly in the form of byte stream after receiving the device message)
        /// Parse binary raw data
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string receiveData(byte[] bytes)
        {
            try
            {
                byte[] newBytes = null;
                if (endbytes != null && endbytes.Length > 0)
                {
                    newBytes = new byte[endbytes.Length + bytes.Length];
                    newBytes = CommonUtil.CombineBytes(endbytes, bytes);
                }
                else
                {
                    newBytes = new byte[bytes.Length];
                    newBytes = bytes;
                }
                //Initialize byte [] subscript
                int i = 0;
                //Data after removing the data before the correct header
                byte[] parserBytes = null;
                foreach (var item in newBytes)
                {
                    if (item == 0x7E)
                    {
                        parserBytes = new byte[newBytes.Length - i];
                        parserBytes = newBytes.Skip(i).Take(newBytes.Length - i).ToArray();
                        return decodeBinaryMessage(parserBytes);
                    }
                    i++;
                }
                return null;
            }
            catch (Exception ex) {
                return null;
            }
        }

        /// <summary>
        /// Parse data
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private static string decodeBinaryMessage(byte[] bytes)
        {
            byte[] frame = PacketUtil.decodePacket(bytes);
            if (frame == null)
            {
                endbytes = bytes;
                return null;
            }
            else
            {
                endbytes = null;
            }
            //Define data entity class
            Result model = new Result();
            //Message ID
            int msgId = CommonUtil.SwapUInt16(BitConverter.ToUInt16(frame, 1));
            model.MsgType = CommonUtil.formatMessageId(msgId);
            //Message properties
            int msgBodyAttr = CommonUtil.SwapUInt16(BitConverter.ToUInt16(frame, 3));
            //Message body length
            int msgBodyLen = msgBodyAttr & 0x03FF;
            byte[] terminalNumArr = frame.Skip(5).Take(6).ToArray();
            model.DeviceID = CommonUtil.ByteToHexStr(terminalNumArr).TrimStart('0');
            //Message serial number
            int msgFlowId = CommonUtil.SwapUInt16(BitConverter.ToUInt16(frame, 11));
            model.MsgFlowId = msgFlowId;
            //Message body
            byte[] msgBodyArr = frame.Skip(13).Take(msgBodyLen).ToArray();
            decoderJt808Message(model, msgBodyArr, msgId, terminalNumArr);
            return JsonConvert.SerializeObject(model);
        }

        /// <summary>
        /// message processing
        /// </summary>
        /// <param name="model"></param>
        /// <param name="msgBodyArr"></param>
        /// <param name="msgId"></param>
        /// <param name="terminalNumArr"></param>
        private static void decoderJt808Message(Result model, byte[] msgBodyArr,int msgId,byte[] terminalNumArr)
        {
            switch (msgId)
            {
                case 0x0200:
                    parseLocationMessage(model, msgBodyArr, msgId, terminalNumArr);
                    break;
                case 0x0001:
                    parse0001Message(model, msgBodyArr);
                    break;
                case 0x0002:
                    parse0002Message(model, msgId, terminalNumArr);
                    break;
                case 0x0100:
                    parse0100Message(model, msgBodyArr, terminalNumArr);
                    break;
                case 0x0102:
                    parse0102Message(model, msgBodyArr, msgId, terminalNumArr);
                    break;
                case 0x0104:
                    parse0104Message(model, msgBodyArr);
                    break;
                case 0x0900:
                    parse0900Message(model, msgBodyArr, msgId, terminalNumArr);
                    break;
                default:
                    model.MsgType = CommonUtil.formatMessageId(msgId);
                    break;
            }
        }

        /// <summary>
        /// Processing location packets
        /// </summary>
        /// <param name="model"></param>
        /// <param name="msgBodyArr"></param>
        /// <param name="msgId"></param>
        /// <param name="terminalNumArr"></param>
        private static void parseLocationMessage(Result model, byte[] msgBodyArr, int msgId, byte[] terminalNumArr)
        {
            LocationData locationData = PacketUtil.parseLocationBody(msgBodyArr);
            string replyMsg = ReplayUtil.reply8001Message(terminalNumArr, model.MsgFlowId, msgId);
            model.DataBody =locationData;
            model.ReplyMsg =replyMsg;
        }

        /// <summary>
        /// General equipment response
        /// </summary>
        /// <param name="model"></param>
        /// <param name="msgBodyArr"></param>
        /// <param name="msgId"></param>
        private static void parse0001Message(Result model, byte[] msgBodyArr) {
            GeneralResponse generalResponse = PacketUtil.parseGeneralResponse(msgBodyArr);
            model.DataBody = generalResponse;
        }

        /// <summary>
        /// Heartbeat
        /// </summary>
        /// <param name="model"></param>
        /// <param name="msgId"></param>
        /// <param name="terminalNumArr"></param>
        private static void parse0002Message(Result model,int msgId, byte[] terminalNumArr)
        {
            //Get message reply content
            string replyMsg = ReplayUtil.reply8001Message(terminalNumArr, model.MsgFlowId, msgId);
            model.ReplyMsg = replyMsg;
        }

        /// <summary>
        /// Vehicle registration information
        /// </summary>
        /// <param name="model"></param>
        /// <param name="msgBodyArr"></param>
        /// <param name="terminalNumArr"></param>
        private static void parse0100Message(Result model, byte[] msgBodyArr,byte[] terminalNumArr)
        {
            DeviceRegistration deviceRegistration = PacketUtil.parseDeviceRegistration(msgBodyArr);
            model.DataBody = deviceRegistration;
            string replyMsg = ReplayUtil.reply8100Message(terminalNumArr, model.MsgFlowId);
            model.ReplyMsg = replyMsg;
        }

        /// <summary>
        /// Terminal authentication
        /// </summary>
        /// <param name="model"></param>
        /// <param name="msgBodyArr"></param>
        /// <param name="msgId"></param>
        /// <param name="terminalNumArr"></param>
        private static void parse0102Message(Result model, byte[] msgBodyArr, int msgId, byte[] terminalNumArr)
        {
            string authenticationCode = CommonUtil.ByteToASCII(msgBodyArr);
            model.DataBody = authenticationCode;
            string replyMsg = ReplayUtil.reply8001Message(terminalNumArr, model.MsgFlowId, msgId);
            model.ReplyMsg = replyMsg;
        }

        /// <summary>
        /// Query device response
        /// </summary>
        /// <param name="model"></param>
        /// <param name="msgBodyArr"></param>
        private static void parse0104Message(Result model, byte[] msgBodyArr)
        {
            DeviceParameter deviceParameter = PacketUtil.parseDeviceParameter(msgBodyArr);
            model.DataBody = deviceParameter;
        }

        /// <summary>
        /// Uplink transparent data
        /// </summary>
        /// <param name="model"></param>
        /// <param name="msgBodyArr"></param>
        private static void parse0900Message(Result model, byte[] msgBodyArr, int msgId, byte[] terminalNumArr)
        {
            TransparentMessage transparentMessage = PacketUtil.parseTransparentMessage(msgBodyArr);
            model.DataBody = transparentMessage;
            if (transparentMessage.MsgType == 0x53 || transparentMessage.MsgType == 0x54) {
                string replyMsg = ReplayUtil.reply8001Message(terminalNumArr, model.MsgFlowId, msgId);
                model.ReplyMsg = replyMsg;
            }
        }
    }
}
